﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;

namespace PlusPlusPaint.GUI
{
    class SketchWindow : Form
    {
        #region member variables
        string _currentToolChoice = "BrushTool";
        Color _currentColour = Color.Black;
        bool _fill = true;
        int _brushSize = 1;
        IDictionary<string, Func<Tools.ISketchTool>> toolBuilders;
        MenuStrip menuStrip;
        SketchControl sketchControl;
        LayerPanelControl layerPanelControl;
        Panel actionPanel;
        ResourceManager resourcemanager
            = new ResourceManager("PlusPlusPaint.Properties.Resources"
                                 , Assembly.GetExecutingAssembly()
                                 );

        #endregion

        #region properties
        string currentToolChoice
        {
            get
            {
                return _currentToolChoice;
            }
            set
            {
                _currentToolChoice = value;
                refreshTool();
            }
        }

        Color currentColour
        {
            get
            {
                return this._currentColour;
            }
            set
            {
                this._currentColour = value;
                refreshTool();
            }
        }

        bool fill
        {
            get
            {
                return _fill;
            }
            set
            {
                _fill = value;
                refreshTool();
            }
        }

        int brushSize
        {
            get
            {
                return _brushSize;
            }
            set
            {
                _brushSize = value;
                refreshTool();
            }
        }

        public Layers.LayerManager LayerManager
        {
            get;
            private set;
        }
        #endregion

        #region event handlers
        private void handleResize(object sender, EventArgs ea)
        {
            sketchControl.Size = new Size ( this.ClientSize.Width  - 220
                                          , this.ClientSize.Height - 60);
            actionPanel.Location = new Point(64, this.ClientSize.Height - 40);
            layerPanelControl.Size = new Size(140, this.ClientSize.Height - 60);
            layerPanelControl.Location = new Point(this.ClientSize.Width - 150, 10);
            layerPanelControl.SketchSize = sketchControl.Size;
            LayerManager.Invalidate();
        }

        private void exit(object sender, EventArgs ea)
        {
            this.Close();
        }

        private void loadSketch(object sender, EventArgs ea)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.DefaultExt = "txt";
                openFileDialog.FileName = "NewSketch";
                openFileDialog.Filter = "Text file|*.txt";
                openFileDialog.Title = "Open File";
                openFileDialog.FileOk += (object o, CancelEventArgs fea) =>
                    {
                        using (StreamReader sr = new StreamReader(openFileDialog.FileName))
                        {
                            try
                            {
                                FileIO.LayeredDataReader dataReader = new FileIO.LayeredDataReader(sr);
                                LayerManager.Deserialize(dataReader.Read());
                            }
                            catch (Exception e)
                            {
                                MessageBox.Show(e.Message);
                            }
                        }
                    };
                openFileDialog.ShowDialog();
            }
        }

        private void saveAsFile(object sender, EventArgs ea)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.DefaultExt = "txt";
            saveFileDialog.FileName = "NewSketch";
            saveFileDialog.Filter = "Text file|*.txt";
            saveFileDialog.Title = "Save File";
            saveFileDialog.FileOk += (object o, CancelEventArgs cea) =>
            {
                using (StreamWriter sw = new StreamWriter(saveFileDialog.FileName))
                {
                    FileIO.LayeredDataWriter dataWriter = new FileIO.LayeredDataWriter(sw);
                    dataWriter.Write(LayerManager.Serialize());
                }
            };

            saveFileDialog.ShowDialog();
        }

        private void saveAsImage(object sender, EventArgs ea)
        {
            SaveFileDialog saveImageDialog = new SaveFileDialog();
            saveImageDialog.DefaultExt = "png";
            saveImageDialog.FileName = "NewSketch";
            saveImageDialog.Filter = "Bitmap Image|*.bmp|PNG Image|*.png|JPeg Image|*.jpg|Gif Image|*.gif";
            saveImageDialog.Title = "Save Image";
            saveImageDialog.FileOk += (object o, CancelEventArgs cea) =>
            {
                Image img = new Bitmap(this.sketchControl.ClientSize.Width, this.sketchControl.ClientSize.Height);
                Graphics gr = Graphics.FromImage(img);
                gr.FillRectangle(Brushes.White, 0, 0, img.Width, img.Height);
                gr.DrawImage(LayerManager.GetVisualRepresentationWithDimensions(img.Size), new Point());
                switch (saveImageDialog.FilterIndex)
                {
                    case 1:
                        img.Save(saveImageDialog.FileName, ImageFormat.Bmp);
                        break;

                    case 2:
                        img.Save(saveImageDialog.FileName, ImageFormat.Png);
                        break;

                    case 3:
                        img.Save(saveImageDialog.FileName, ImageFormat.Jpeg);
                        break;

                    case 4:
                        img.Save(saveImageDialog.FileName, ImageFormat.Gif);
                        break;
                }
            };

            saveImageDialog.ShowDialog();
        }
        #endregion

        #region constructors
        public SketchWindow()
        {
            this.ClientSize = new Size(700, 500);
            this.LayerManager = new Layers.LayerManager();

            sketchControl = new SketchControl(this.LayerManager);
            sketchControl.Location = new Point(64, 10);
            this.Controls.Add(sketchControl);

            toolBuilders = new Dictionary<string, Func<Tools.ISketchTool>>();
            toolBuilders["BrushTool"] = () => new Tools.BrushTool();
            toolBuilders["ObjectRemovalTool"] = () => new Tools.ObjectRemovalTool();
            toolBuilders["TextEditingTool"] = () => new Tools.TextEditingTool();
            toolBuilders["StraightLineTool"] = () => new Tools.StraightLineTool();
            toolBuilders["FilledRectangleTool"] = () => new Tools.FilledRectangleTool();
            toolBuilders["EmptyRectangleTool"] = () => new Tools.EmptyRectangleTool();
            toolBuilders["FilledEllipseTool"] = () => new Tools.FilledEllipseTool();
            toolBuilders["EmptyEllipseTool"] = () => new Tools.EmptyEllipseTool();

            this.currentToolChoice = "BrushTool";
            this.sketchControl.ToolInvalid += (object sender, EventArgs ea) =>
                {
                    this.refreshTool();
                };

            layerPanelControl = new LayerPanelControl(this.LayerManager);
            this.Controls.Add(layerPanelControl);
            
            this.KeyPreview = true;
            this.KeyDown += (object sender, KeyEventArgs kea) => { if (kea.Control) layerPanelControl.CtrlDown = true; };
            this.KeyUp += (object sender, KeyEventArgs kea) => layerPanelControl.CtrlDown = false;


            menuStrip = new MenuStrip();
            menuStrip.Visible = false;
            this.Controls.Add(menuStrip);
            this.makeFileMenu();
            this.makeToolsMenu(toolBuilders);
            this.makeActionsMenu();
            this.makeToolButtons(toolBuilders);
            this.makeActionButtons();
            this.Resize += this.handleResize;
            this.handleResize(null, null);
            this.WindowState = FormWindowState.Maximized;
        }
        #endregion

        #region menus and buttons
        private void makeFileMenu()
        {   
            ToolStripMenuItem menu = new ToolStripMenuItem("File");
            menu.MergeAction = MergeAction.MatchOnly;
            menu.DropDownItems.Add("Load File", null, this.loadSketch);
            menu.DropDownItems.Add("Save as Image...", null, this.saveAsImage);
            menu.DropDownItems.Add("Save as File", null, this.saveAsFile);
            menu.DropDownItems.Add("Close", null, this.exit);
            menuStrip.Items.Insert(0, menu);
        }

        private void makeToolsMenu(IDictionary<string, Func<Tools.ISketchTool>> tools)
        {   
            ToolStripMenuItem menu = new ToolStripMenuItem("Tool");
            foreach (string toolname in tools.Keys)
            {   ToolStripItem item = new ToolStripMenuItem();
                item.Tag = toolname;
                item.Text = toolname;
                item.Image = (Image)resourcemanager.GetObject(toolname);
                string toolchosen = toolname; // Necessary for lambda to behave correctly.
                item.Click += (object sender, EventArgs mea) =>
                {
                    this.currentToolChoice = toolchosen;
                };
                menu.DropDownItems.Add(item);
            }
            menuStrip.Items.Add(menu);
        }

        private void makeActionsMenu()
        {   
            ToolStripMenuItem menu = new ToolStripMenuItem("Action");
            menu.DropDownItems.Add("Clear Layers", null, sketchControl.Clear );
            menuStrip.Items.Add(menu);
        }

        private void makeToolButtons(IDictionary<string, Func<Tools.ISketchTool>> toolBuilders)
        {
            int t = 0;
            foreach (string toolname in toolBuilders.Keys)
            {
                RadioButton b = new RadioButton();
                b.Appearance = Appearance.Button;
                b.Size = new Size(45, 45);
                b.Location = new Point(10, 10 + t * 45);
                b.Tag = toolname;
                //b.Text = toolname;
                b.Image = (Image)resourcemanager.GetObject(toolname);
                b.TextAlign = ContentAlignment.TopCenter;
                b.ImageAlign = ContentAlignment.BottomCenter;
                string toolchosen = toolname; // necessary for lambda to behave correctly.
                b.Click += (object sender, EventArgs mea) =>
                    {
                        this.currentToolChoice = toolchosen;
                    };
                this.Controls.Add(b);
                if (t == 0) b.Select();
                t++;
            }
        }

        private void makeActionButtons()
        {
            Color[] colours = { Color.Black, Color.White, Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Magenta, Color.Cyan };

            actionPanel = new Panel();
            actionPanel.Size = new Size(600, 40);
            this.Controls.Add(actionPanel);

            #region ColourPanels
            {
                Panel ccp = new Panel();
                ccp.BorderStyle = BorderStyle.FixedSingle;
                ccp.Location = new Point(280, 0);
                ccp.Size = new Size(100, 38);
                actionPanel.Controls.Add(ccp);

                Label ccl = new Label();
                ccl.Text = "Selected Colour:";
                ccl.Location = new Point(5, 2);
                ccl.AutoSize = true;
                ccp.Controls.Add(ccl);

                Label ccil = new Label();
                ccil.Text = "0,0,0";
                ccil.Location = new Point(26, 20);
                ccil.AutoSize = true;
                ccp.Controls.Add(ccil);

                ColourBox ccb = new ColourBox(Color.Black);
                ccb.Location = new Point(8, 19);
                ccb.ColourUpdated += (object sender, EventArgs ea) => 
                {
                    this.currentColour = ccb.Colour;
                    ccil.Text = ccb.Colour.R.ToString() + "," + ccb.Colour.G.ToString() + "," + ccb.Colour.B.ToString();
                };
                ccp.Controls.Add(ccb);

                Panel sp = new Panel();
                sp.BorderStyle = BorderStyle.FixedSingle;
                sp.Location = new Point(400, 0);
                sp.Size = new Size(130, 38);
                actionPanel.Controls.Add(sp);

                Label sl = new Label();
                sl.Text = "Swatch:";
                sl.Location = new Point(3, 10);
                sl.AutoSize = true;
                sp.Controls.Add(sl);

                for (int cCount = 0; cCount < colours.Length; cCount++)
                {
                    ColourBox cb = new ColourBox(colours[cCount]);
                    cb.Location = new Point(52 + cCount / 2 * 20, 2 + (cCount % 2) * 18);
                    cb.Click += (object sender, EventArgs ea) => ccb.Colour = cb.Colour;
                    cb.ColourUpdated += (object sender, EventArgs ea) => ccb.Colour = cb.Colour;
                    sp.Controls.Add(cb);
                }
            }
            #endregion

            #region BrushSizePanel
            {
                Panel bsp = new Panel();
                bsp.BorderStyle = BorderStyle.FixedSingle;
                bsp.Location = new Point(140, 0);
                bsp.Size = new Size(120, 38);
                actionPanel.Controls.Add(bsp);

                Label bsl = new Label();
                bsl.Text = "Brush Size: 1 px";
                bsl.Location = new Point(3, 2);
                bsl.AutoSize = true;
                bsp.Controls.Add(bsl);

                TrackBar tb = new TrackBar();
                tb.Location = new Point(4, 12);
                tb.Size = new Size(110, 16);
                tb.Minimum = 1;
                tb.Maximum = 50;
                tb.ValueChanged += (object sender, EventArgs ea) =>
                {
                    bsl.Text = "Brush Size: " + tb.Value.ToString() + " px";
                    this.brushSize = tb.Value;
                };
                bsp.Controls.Add(tb);
            }
            #endregion
        }

        private void refreshTool()
        {
            this.sketchControl.CurrentTool = toolBuilders[currentToolChoice]();
            this.sketchControl.CurrentTool.Colour = this.currentColour;
            this.sketchControl.CurrentTool.BrushSize = this.brushSize;
        }
        #endregion
    }
}
